﻿Option Explicit On
Option Strict On

Imports System
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Text

''' <summary>
''' 
''' </summary>
''' <remarks></remarks>
Public Class VerticalString


#Region " Enums "

    Private Enum esaType
        Pre
        Post
        Either
    End Enum

#End Region

#Region " Private Members "

    Private m_textSpread As Double

#End Region

#Region " Public Properties "

    Public Property TextSpread As Double
        Get
            Return m_textSpread
        End Get
        Set(ByVal value As Double)
            m_textSpread = value
        End Set
    End Property

#End Region

#Region " Constructor "

    ''' <summary>
    ''' Default Constructor 
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        m_textSpread = 0.75F
    End Sub

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' draw string in top left of rectangle
    ''' </summary>
    ''' <param name="g"></param>
    ''' <param name="text"></param>
    ''' <param name="font"></param>
    ''' <param name="brush"></param>
    ''' <param name="stringRect"></param>
    ''' <remarks></remarks>
    Public Sub Draw(ByVal g As Graphics, ByVal text As String, ByVal font As Font, ByVal brush As Brush, ByVal stringRect As Rectangle)
        Me.Draw(g, text, font, brush, stringRect.X, stringRect.Y)
    End Sub

    ''' <summary>
    ''' draw string in rectangle according to Alignment and LineAlignment
    ''' </summary>
    ''' <param name="g"></param>
    ''' <param name="text"></param>
    ''' <param name="font"></param>
    ''' <param name="brush"></param>
    ''' <param name="stringRect"></param>
    ''' <param name="StringFmt"></param>
    ''' <remarks></remarks>
    Public Sub Draw(ByVal g As Graphics, ByVal text As String, ByVal font As Font, ByVal brush As Brush, ByVal stringRect As Rectangle, ByVal StringFmt As StringFormat)

        'Validate Parameters
        If font Is Nothing Then
            Throw New ArgumentNullException("font")
        End If

        If StringFmt Is Nothing Then
            Throw New ArgumentNullException("StringFmt")
        End If

        Dim horOffset As Integer
        Dim vertOffset As Integer

        'Set horizontal offset
        Select Case StringFmt.Alignment
            Case StringAlignment.Center
                horOffset = CInt(stringRect.Width / 2) - CInt((font.Size / 2)) - 2
            Case StringAlignment.Far
                horOffset = (stringRect.Width - CInt(font.Size) - 2)
            Case Else
                horOffset = 0
        End Select

        'Set vertical offset
        Dim textSize As Double = Me.Length(text, font)

        Select Case StringFmt.LineAlignment
            Case StringAlignment.Center
                vertOffset = CInt(stringRect.Height / 2) - CInt(textSize / 2)
            Case StringAlignment.Far
                vertOffset = stringRect.Height - CInt(textSize) - 2
            Case Else
                vertOffset = 0
        End Select

        'Draw the string using the offsets
        Me.Draw(g, text, font, brush, stringRect.X + horOffset, stringRect.Y + vertOffset)
    End Sub

    ''' <summary>
    ''' draw string at coordinates x, y
    ''' </summary>
    ''' <param name="g"></param>
    ''' <param name="text"></param>
    ''' <param name="font"></param>
    ''' <param name="brush"></param>
    ''' <param name="x"></param>
    ''' <param name="y"></param>
    ''' <remarks></remarks>
    Public Sub Draw(ByVal g As Graphics, ByVal text As String, ByVal font As Font, ByVal brush As Brush, ByVal x As Integer, ByVal y As Integer)

        'Validate Parameters
        If font Is Nothing Then
            Throw New ArgumentNullException("font")
        End If

        If g Is Nothing Then
            Throw New ArgumentNullException("g")
        End If

        If text Is Nothing Then
            Throw New ArgumentNullException("text")
        End If

        Dim textChars() As Char = text.ToCharArray() ' Put the string into array of chars
        Using charStrFmt As StringFormat = New StringFormat() ' Used to align each char centrally
            charStrFmt.Alignment = StringAlignment.Center
            Dim charRect As Rectangle = New Rectangle(x, y, CInt(font.Size * 1.5), font.Height)

            For i As Integer = 0 To text.Length - 1
                charRect.Offset(0, ExtraSpaceAllowance(esaType.Pre, textChars(i), font)) ' Height allowance
                g.DrawString(textChars(i).ToString(), font, brush, charRect, charStrFmt) ' Write the character
                charRect.Offset(0, CInt(font.Height * TextSpread)) ' Standard height
                charRect.Offset(0, ExtraSpaceAllowance(esaType.Post, textChars(i), font)) ' Height Allowance
            Next
        End Using
    End Sub

    ''' <summary>
    ''' returns vertical length of string
    ''' </summary>
    ''' <param name="text"></param>
    ''' <param name="font"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Length(ByVal text As String, ByVal font As Font) As Integer
        If font Is Nothing Then
            Throw New ArgumentNullException("font")
        End If
        If text Is Nothing Then
            Throw New ArgumentNullException("text")
        End If

        Dim textChars() As Char = text.ToCharArray() ' Put the string into array of chars
        Dim len As Integer

        For i As Integer = 0 To text.Length - 1
            len += CInt(font.Height * TextSpread) ' Add height of font, times spread factor.
            len += ExtraSpaceAllowance(esaType.Either, textChars(i), font) ' Add allowance
        Next

        Return len
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="type"></param>
    ''' <param name="ch"></param>
    ''' <param name="font"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ExtraSpaceAllowance(ByVal type As esaType, ByVal ch As Char, ByVal font As Font) As Integer

        If (TextSpread >= 1) Then Return 0 ' No action if textSpread 1 or more

        Dim offset As Integer = 0

        ' Do we need to pad BEFORE the next char?
        If type = esaType.Pre Or type = esaType.Either Then
            ' Does our character appear in the "pre" list? (ie taller than average)
            If (" bdfhijkltABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".IndexOf(ch) > 0) Then
                offset += CInt(font.Height * 0.2)
            End If
        End If

        ' Do we need to pad AFTER the next char?
        If type = esaType.Post Or type = esaType.Either Then
            ' Does our character appear in the "post" ltis? (is dangles over the bottom of the line)
            If (" gjpqyQ".IndexOf(ch) > 0) Then
                offset += CInt(font.Height * 0.2)
            End If
        End If

        Return offset
    End Function

#End Region

End Class


